// Copyright (c) 2025 Yao Yu
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

extern crate alloc;
use align_ext::AlignExt;
use super::{Thread, AsThread};
use alloc::{boxed::Box, sync::Arc};
use ostd::{
    arch::qemu::{exit_qemu, QemuExitCode},
    cpu::context::UserContext,
    Error,
    mm::{
        CachePolicy, FrameAllocOptions, PageFlags, PageProperty,
        Vaddr, VmIo, VmSpace, PAGE_SIZE,
    },
    prelude::*,
    task::{disable_preempt, Task, TaskOptions},
    user::{ReturnReason, UserMode},
};
use crate::{
    context::Context,
    process::elf::{check_elf_header, get_entry_point},
    syscall::handle_syscall,
    thread::{AsThreadLocal, ThreadLocal},
};

// 用户程序的线程任务
pub fn create_user_task(thread_ref: Arc<Thread>, user_ctx: Arc<UserContext>, thread_local: ThreadLocal) -> Task {
	// 这里函数定义，并不会立即调用
    fn user_task() {
		// 获取内核当前CPU执行的当前任务
        let current = Task::current().unwrap();
        let current_thread = current.as_thread().unwrap();
        let current_thread_local = current.as_thread_local().unwrap();

        // Switching between user-kernel space is
        // performed via the UserMode abstraction.
        // 通过UserMode抽象，完成用户-内核空间切换
        // 获得用户空间，包含UserSpace的原子引用计数指针 和 当前用户上下文
        let user_ctx = current.user_ctx().expect("user task should have user context");
        let mut user_mode = UserMode::new(UserContext::clone(user_ctx));
		
        let context = Context::new(current_thread, current_thread_local);

		// 这里循环执行的就是先switch到用户空间执行代码（执行用户程序），等到有trap时就执行下面的处理代码
        loop {
            // 当系统调用或CPU例外发生或一些内核指定的事件发生 时 下面的execute方法返回
            let return_reason = user_mode.execute(|| false);

            // 用户空间的CPU寄存器可以通过UserContext抽象来访问和控制
            let user_context = user_mode.context_mut();
            // let vm_space = current_thread.vm_space().unwrap();
            if ReturnReason::UserSyscall == return_reason {
                handle_syscall(&context, user_context);
                // user_context.set_rax(sys_ret as usize);
            } else if ReturnReason::UserException == return_reason {
                let info = user_context.trap_information();
                println!("user exception: {:?} {:?} {:?}", info.id, info.error_code, info.page_fault_addr);
                exit_qemu(QemuExitCode::Failed);

            } else {
                println!("panic: unexpected return reason");
                exit_qemu(QemuExitCode::Failed);
            }
            // 线程退出
            if current_thread.is_exited(){
                break;
            }
        }
    }

    // Kernel tasks are managed by the Framework,
    // while scheduling algorithms for them can be
    // determined by the users of the Framework.
    // 内核任务由框架管理，而它们的调度算法由框架的用户决定（这里指开发者？算法已经内置到框架中了吗）
    TaskOptions::new(user_task)
    .data(thread_ref)
    .local_data(thread_local)
    .user_ctx(Some(user_ctx))
    .build()
    .unwrap()
}

/// 输入用户程序（字节数组不可变引用切片，只读），为用户进程分配内存页面，映射页表，生成CPU上下文（程序入口地址）
pub fn create_vm_space(program: &[u8]) -> Result<(VmSpace, UserContext)> {
	// 根据程序内存占用，计算内存占用并向上对其到PAGE_SIZE的整数倍
    let nbytes = program.len().align_up(PAGE_SIZE);
    // 用户页面
    let user_pages = {
        let segment = FrameAllocOptions::new().alloc_segment(nbytes / PAGE_SIZE)?;
        
        // 物理内存页只能通过UFrame或USegment抽象来访问
        // 物理页面全部写0清空
        segment.write_bytes(0, program)?;
        segment
    };
    // 保护页
    let ustack_guard_page = {
        let page = FrameAllocOptions::new().alloc_frame()?;
        page.write_bytes(0, &[0; PAGE_SIZE])?;
        page
    };
    // 用户栈
    let ustack = {
        let ustack_seg = FrameAllocOptions::new().alloc_frame()?;
        ustack_seg.write_bytes(0, &[0; PAGE_SIZE])?;
        ustack_seg
    };
    // 用户地址空间
    let (vm_space, ustack_top) = {
        const MAP_ADDR: Vaddr = 0x0040_0000;

        // The page table of the user space can be
        // created and manipulated safely through
        // the `VmSpace` abstraction.
        // 用户空间页表能通过VmSpace抽象来安全地创建和控制
        let vm_space = VmSpace::new();
        // cursor独占拥有页表的子树，阻止其他cursor创建，因此尽快丢弃cursor
        // 页表由唯一的cursor来守卫，如果另一个cursor与此cursor的地址范围重叠，创建cursor会被阻塞。
        let preempt_guard = disable_preempt();
        let mut cursor = vm_space.cursor_mut(&preempt_guard, &(MAP_ADDR..MAP_ADDR + nbytes))?;
        // 页面映射属性配置，读写执行权限，WriteBack回写缓存机制，适合普通内存
        let map_prop = PageProperty::new_user(PageFlags::RWX, CachePolicy::Writeback);
        for frame in user_pages {
            cursor.map(frame.into(), map_prop);
        }
        drop(cursor);
        
        // 光标需要使用新的，因为映射虚拟地址范围不同了。
        let guard_page_vaddr: Vaddr = MAP_ADDR+nbytes;
        let mut cursor = vm_space.cursor_mut(&preempt_guard, &(guard_page_vaddr..guard_page_vaddr+PAGE_SIZE*2))?;
        // 为保护页创建新的映射属性，
        let uguard_map_prop = PageProperty::new_absent();
        // 映射栈保护页和用户栈
        cursor.map(ustack_guard_page.into(), uguard_map_prop);
        cursor.map(ustack.into(), map_prop);
        drop(cursor);

        // 返回一个原子引用计数的 经过页面分配和映射的 虚拟地址空间
        (vm_space, guard_page_vaddr + PAGE_SIZE*2)
    };
    
    let user_cpu_state = {
        // 用户空间CPU状态可以通过UserContext抽象来初始化成任意值
        let mut user_cpu_state = UserContext::default();
        let buf = {
            let mut header_buffer = Box::new([0u8; PAGE_SIZE]);
            header_buffer.copy_from_slice(&program[..PAGE_SIZE]);
            header_buffer
        };
        // 检查elf文件头是否符合当前内核要求
        let _ = check_elf_header(&*buf);
        // 获得程序入口
        let entry_point = get_entry_point(&*buf)
            .map_err(|_| Error::AccessDenied)?;
        // 设置x86_64程序入口地址为elf header中的入口地址，通过改变rip寄存器值来完成。
        user_cpu_state.set_rip(entry_point);
        user_cpu_state.set_rsp(ustack_top);
        user_cpu_state
    };
    // 返回由 新的用户地址空间 和 用户初始化时候的CPU上下文 组成的用户空间
    Ok((vm_space, user_cpu_state))
}
